package com.york.tinyrpc.protoss.proxy;

import cn.hutool.core.collection.CollUtil;
import com.york.tinyrpc.protoss.TinyRpcApplication;
import com.york.tinyrpc.protoss.config.RegistryCenterConfig;
import com.york.tinyrpc.protoss.constant.TinyRpcConstants;
import com.york.tinyrpc.protoss.fault.retry.RetryStrategy;
import com.york.tinyrpc.protoss.fault.retry.RetryStrategyFactory;
import com.york.tinyrpc.protoss.fault.tolerant.TolerantStrategy;
import com.york.tinyrpc.protoss.fault.tolerant.TolerantStrategyFactory;
import com.york.tinyrpc.protoss.loadbalancer.LoadBalancer;
import com.york.tinyrpc.protoss.loadbalancer.LoadBalancerFactory;
import com.york.tinyrpc.protoss.model.ServiceMetaInfo;
import com.york.tinyrpc.protoss.model.TinyRpcRequest;
import com.york.tinyrpc.protoss.model.TinyRpcResponse;
import com.york.tinyrpc.protoss.registry.center.RegistryCenter;
import com.york.tinyrpc.protoss.registry.center.RegistryCenterFactory;
import com.york.tinyrpc.protoss.serializer.Serializer;
import com.york.tinyrpc.protoss.serializer.SerializerFactory;
import com.york.tinyrpc.protoss.server.tcp.VertxTcpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 基于JDK的动态代理实现
 * 灵活处理所有接口类型的代理对象去执行
 */
public class TinyRpcServiceHandler implements InvocationHandler {

    private static final Logger log = LoggerFactory.getLogger(TinyRpcServiceHandler.class);


    /**
     * 调用代理
     *
     * @param proxy  代理对象
     * @param method 被代理对象的方法
     * @param args   被代理对象方法的参数
     * @return Object 代理对象运行结果
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getSerializer(TinyRpcApplication.getRpcConfig().getSerializer());

        // 构造请求，TinyRpcRequest实体类由@Builder注解，编译会生成建造者模式，可以一些列...的方式生成对象
        TinyRpcRequest rpcRequest = TinyRpcRequest.builder()
                .serviceName(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();

        try {
            // 通过TinyRpc启动类得到注册中心的相关配置，获取注册中心Bean
            RegistryCenterConfig registryCenterConfig = TinyRpcApplication.getRpcConfig().getRegistryCenterConfig();
            RegistryCenter registryCenter = RegistryCenterFactory.getRegistryCenterByType(registryCenterConfig.getRegistryCenterType());

            // 构建欲调用的相关服务元信息
            ServiceMetaInfo serviceMetaInfo = ServiceMetaInfo.builder().serviceName(rpcRequest.getServiceName())
                    .serviceVersion(TinyRpcConstants.DEFAULT_SYSTEM_VERSION).build();

            //从注册中心查找相关的服务列表以及服务信息
            List<ServiceMetaInfo> discoveryServiceMetaInfos = registryCenter.serviceDiscovery(serviceMetaInfo.getServiceKey());

            // 从服务发现列表中筛选（负载均衡）服务，执行
            if (CollUtil.isEmpty(discoveryServiceMetaInfos)) {
                throw new RuntimeException("TinyRPC调用失败...本次调用暂无可用服务地址");
            }
            // todo 补充负载均衡算法，从服务发现列表中筛选服务
            // 已修正，利用负载均衡器选择目标服务
            // 获取负载均衡器
            LoadBalancer loadBalancer = LoadBalancerFactory.getLoadBalancerByType(TinyRpcApplication.getRpcConfig().getLoadBalancer());
            log.info("本次执行选用loadBalancer: {}", loadBalancer);
            // 构建requestParams，当前只有方法名
            Map<String, Object> requestParams = new HashMap<>();
            requestParams.put("methodName", rpcRequest.getMethodName());
            ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.getTargetService(requestParams, discoveryServiceMetaInfos);

            // protoss版本：已解决“proto版本：发送请求，硬编码方式（需要使用注册中心和服务发现机制解决”
            // protoss版本：改为用自拟协议发送TCP请求而不是HTTP请求,Client端也就是消费方，由消费方发起请求，由VertxTcpClient将请求序列化
            TinyRpcResponse rpcResponse;
            try {
                // 封装重试策略
                RetryStrategy retryStrategy =
                        RetryStrategyFactory.getRetryStrategyByType(TinyRpcApplication.getRpcConfig().getRetryStrategy());
                log.info("本次执行选用retryStrategy: {}", retryStrategy);
                rpcResponse = retryStrategy.doRetry(() ->
                        VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo));
                log.info("TinyRpc调用成功 rpcResponse:{}", rpcResponse.getData());
            } catch (Exception e) {
                // 封装容错策略
                TolerantStrategy tolerantStrategy =
                        TolerantStrategyFactory.getTolerantStrategyByType(TinyRpcApplication.getRpcConfig().getTolerantStrategy());
                // 封装上下文逻辑
                Map<String, Object> context = new HashMap<>();
                // 将可选的服务列表存入上下文，剔除本次异常调用的服务
                discoveryServiceMetaInfos.remove(selectedServiceMetaInfo);
                // 如果移除之后没有可选服务，那就不传入了,用于Fail-Over故障转移
                if (!discoveryServiceMetaInfos.isEmpty()) {
                    context.put(TinyRpcConstants.VALID_SERVICE_META_INFOS, discoveryServiceMetaInfos);
                    // 顺便传入负载均衡器即负载均衡选择的参数
                    context.put(TinyRpcConstants.TARGET_LOAD_BALANCER, loadBalancer);
                    context.put(TinyRpcConstants.LOAD_BALANCE_PARAMS, requestParams);
                    context.put(TinyRpcConstants.TINY_RPC_REQUEST,rpcRequest);
                }
                rpcResponse = tolerantStrategy.doTolerant(context, e);
            }
            return rpcResponse.getData();
        } catch (Exception e) {
            log.error("TinyRpc调用失败:{}", e.getMessage());
        }

        // 如果执行到这还没有return说明没有调用成功
        return null;
    }
}
